package com.dj.product.k2faas.web.config;

import com.dj.product.k2faas.common.dto.ErrorResponse;
import com.dj.product.k2faas.common.exception.*;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.resource.NoResourceFoundException;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;

/**
 * Created by 丁建 on 2025/9/23 08:35
 *
 * @author 丁建
 */
@ControllerAdvice
@Slf4j
public class DefaultExceptionHandler {

    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ErrorResponse handle(HttpMessageNotReadableException exception) {
        //进不到自己的代码，所以不需要考虑在debug时，返回堆栈信息
        log.error(exception.getMessage(), exception);
        ErrorResponse errorResponse = new ErrorResponse().setStatus(HttpStatus.BAD_REQUEST.value());

        Throwable throwable = exception.getCause();
        if (throwable instanceof JsonParseException) {
            errorResponse.setMessage("Json数据解析出错：" + throwable.getMessage());
            return errorResponse;
        } else if (throwable instanceof MismatchedInputException) {
            errorResponse.setMessage("Json数据格式不匹配：" + throwable.getMessage());
            return errorResponse;
        } else {
            errorResponse.setMessage(exception.getMessage());
        }

        return errorResponse;
    }

    @ExceptionHandler(ForbiddenException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ResponseBody
    public ErrorResponse handle(ForbiddenException exception) {
        log.error(exception.getMessage(), exception);
        return buildResult(exception);
    }

    @ExceptionHandler(BadRequestException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ErrorResponse handle(BadRequestException exception) {
        log.error(exception.getMessage(), exception);
        return buildResult(exception);
    }

    @ExceptionHandler(UnAuthorizedException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ResponseBody
    public ErrorResponse handle(UnAuthorizedException exception) {
        log.error(exception.getMessage(), exception);
        return buildResult(exception);
    }

    @ExceptionHandler(ConflictException.class)
    @ResponseStatus(HttpStatus.CONFLICT)
    @ResponseBody
    public ErrorResponse handle(ConflictException exception) {
        log.error(exception.getMessage(), exception);
        return buildResult(exception);
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public Object handle(MethodArgumentNotValidException exception) {
        log.warn(exception.getMessage(), exception);
        var result = new ErrorResponse();
        List<ObjectError> allErrors = exception.getBindingResult().getAllErrors();
        if (!CollectionUtils.isEmpty(allErrors)) {
            result.setMessage(allErrors.getFirst().getDefaultMessage());
        }
        result.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        return result;
    }

    @ExceptionHandler(NotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ResponseBody
    public ErrorResponse handle(NotFoundException exception) {
        log.error(exception.getMessage(), exception);
        return buildResult(exception);
    }

    @ExceptionHandler(K2Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public ErrorResponse handle(K2Exception exception) {
        log.error(exception.getMessage(), exception);
        return buildResult(exception);
    }

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public ErrorResponse handle(Exception exception) throws Exception {
        //从Spring Boot 3.2.2开始
        if (exception instanceof NoResourceFoundException) {
            throw exception;
        }

        log.error(exception.getMessage(), exception);
        return buildResult(exception);
    }

    private ErrorResponse buildResult(Exception exception) {
        var result = new ErrorResponse();

        if (exception instanceof K2Exception xianJianException) {
            result.setStatus(xianJianException.getErrorCode());
        } else {
            result.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        }
        result.setMessage(exception.getMessage());
        if (log.isDebugEnabled()) {
            result.setDebugInfo(getStackTrace(exception));
        }
        return result;
    }

    private String getStackTrace(Throwable exception) {
        StringWriter stringWriter = new StringWriter();
        exception.printStackTrace(new PrintWriter(stringWriter));

        return stringWriter.toString();
    }
}
